Теперь мы готовы проверить, действительно ли работает наш код FunC. Как только мы напишем код FunC, мы просто запустим компиляцию пряжи и убедимся, что наш код может работать на TVM.

Смартконтракт в этом уроке будет очень простым, но этого будет достаточно, чтобы ознакомиться с базовым синтаксисом и структурой FunC.

# Параметры, которые мы получаем с помощью внутреннего обработчика сообщений Recv_internal

Давайте возьмемся за код. На предыдущем уроке мы уже создали файл Contracts/main.fc . Давайте откроем его и посмотрим, что у нас там есть:

```c
() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {

}
```

У нас уже есть функция, обрабатывающая входящие сообщения. Как вы уже знаете, любые транзакции в TON называются сообщениями.

Итак, какое послание несет нам? Мы уже видим три параметра, которые передаются в функцию Recv_internal:

- `msg_value` — этот параметр сообщает нам, сколько монет TON (или граммов) получено с этим сообщением.
- `in_msg` — это полное сообщение, которое мы получили, со всей информацией о том, кто его отправил и т. д. Мы видим, что оно имеет тип `Cell`. Что это значит? Тело сообщения хранится как ячейка в TVM, поэтому для нашего сообщения выделена одна целая ячейка со всеми ее данными.
- `in_msg_body` — это собственно «читаемая» часть полученного нами сообщения. Имеет тип среза, поскольку является частью Ячейки, он указывает «адрес», с какой части ячейки нам следует начать чтение, если мы хотим прочитать этот параметр среза.

> ЗАДАЧА: Уточните содержимое ячейки `in_msg` .

Как вы можете видеть, и `msg_value` , и `in_msg_body` являются производными от `in_msg` , но для удобства использования мы получаем их в качестве параметров в функцию `take_internal`.

# Спецификаторы функций

Я уверен, что вы заметили слово `impure` сразу после того, как параметры были переданы в функцию. Это один из трех возможных спецификаторов функции:

- `impure`
- `inline/inline_ref`
- `method_id`
  
Один, несколько или ни один из них не могут быть помещены в объявление функции, но в настоящее время они должны быть представлены в правильном порядке. Например, после `inline` нельзя ставить `impure`.

На данный момент нас интересует только `impure` спецификатор, но мы собираемся охватить остальные, как только они начнут появляться в нашем коде.

`impure` спецификатор означает, что функция может иметь некоторые побочные эффекты, которые нельзя игнорировать. Например, нам следует поставить `impure` спецификатор, если функция может изменять хранилище контракта, отправлять сообщения или выдавать исключение, когда некоторые данные недействительны, и функция предназначена для проверки этих данных.

Если `impure` не указан и результат вызова функции не используется, то компилятор FunC может и удалит этот вызов функции.

# Импорт stdlib.fc

Чтобы манипулировать данными и писать другую логику в нашем контракте, нам нужно сделать еще одну важную вещь. Нам нужно импортировать стандартную библиотеку FunC. На данный момент эта библиотека является всего лишь оберткой для самого распространенного ассемблера невстроенных команд TVM. Описание каждой команды TVM, используемой в библиотеке, можно найти в [документации](https://ton.org/docs/develop/func/stdlib) .

Чтобы импортировать stdlib.fc, давайте создадим папку `imports` внутри папки контрактов. Затем создайте файл stdlib.fc и заполните его содержимым официальной библиотеки stdlib.fc, которую вы можете получить [здесь](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/stdlib.fc).

Теперь в самом начале нашего main.fc нам нужно вставить сам импорт:

```c
#include "imports/stdlib.fc";

() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {

}
```

Отлично, теперь мы готовы идти!

# Разбор `in_msg`

Давайте наконец узнаем, что мы можем делать с параметрами, передаваемыми в нашу функцию `recv_internal`.

Всякий раз, когда мы хотим обработать внутреннее сообщение, прежде чем мы даже начнем читать содержательную часть `in_msg_body`, нам нужно сначала понять, какого типа внутреннее сообщение мы получили. Могут быть разные случаи. Например, мы могли бы получить это сообщение, потому что наш контракт ранее отправил кому-то какое-то сообщение, и принимающая сторона не смогла его принять, поэтому оно «bounced» обратно. Иногда мы не хотим обрабатывать такого рода сообщения. Мы поговорим о таких сценариях позже в этом курсе.

В каждом сообщении, которое мы получаем, есть флаги. Флаги представляют собой, по сути, 4-битное целое число, где каждый бит... (ЗАДАЧА: Разработать структуру флагов. int_msg_info $0 ihr_disabled:Boolounce:Boolounce:Bool).

Эти флаги на самом деле сообщат нам ценную информацию о сообщении, например: «Это сообщение было отклонено получателем, и теперь оно фактически возвращается обратно».

Итак, первое, что должен сделать наш контракт при получении внутреннего сообщения — проанализировать его. Давайте разберем `in_msg`:
```c
() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {
    slice cs = in_msg.begin_parse();
    int flags = cs~load_uint(4);
}
```
Давайте разберем, что именно происходит в этом коде:

Вам нужно будет запомнить эту концепцию: `slice` — это «адрес», указатель. Итак, когда мы парсим — мы парсим, начиная с какого-то места. В этом случае функция `begin_parse()` сообщает нам, с чего нам следует начать анализ, она дает нам указатель на самый первый бит ячейки `in_msg`.

Затем мы анализируем 4-битное целое число, вызывая `load_uint(4)` и присваиваем результат переменной `int flags`.

Как только мы собираемся вызвать еще несколько `~load*{\*}` для переменной `cs`, мы фактически продолжим анализ с того места, где завершился предыдущий `~load*{\*}`.

В случае, если мы попытаемся разобрать что-то, чего на самом деле не существует в ячейке, наш контракт завершится с кодом 9. Подробнее о стандартных ошибках кода можно прочитать [здесь](https://ton.org/docs/learn/tvm-instructions/tvm-exit-codes).

В ячейке `in_msg` есть еще более ценная информация, а именно — адрес отправителя, поэтому продолжим разбор:
```c
() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {
    slice cs = in_msg.begin_parse();
    int flags = cs~load_uint(4);
    slice sender_address = cs~load_msg_addr();
}
```
Когда мы хотим создать переменную, в которой будет храниться адрес, мы всегда используем тип среза, поэтому мы сохраняем только указатель, откуда память должна прочитать адрес, когда он понадобится.

Что мы будем делать с переменными, которые у нас уже есть? Позвольте мне сначала познакомить вас еще с двумя возможностями смартконтракта:
1. Наш смарт-контракт имеет постоянное хранилище (называемое хранилищем c4).
2. Наш смарт-контракт может иметь метод получения, который позволит любому человеку извне мира получить некоторые данные из нашего контракта.

Используя эти две новые возможности, мы можем сделать следующее. Мы можем сохранить адрес отправителя в нашем хранилище и создать метод получения, который будет возвращать значение при вызове.

Другими словами, наш геттер всегда будет возвращать адрес контракта, который отправил сообщение нашему контракту самым последним.

# Использование постоянного хранилища

Чтобы сохранить те же данные в нашем постоянном хранилище c4, мы собираемся использовать стандартную функцию FunC `set_data`. Эта функция принимает и сохраняет ячейку.

> Если мы хотим сохранить больше данных, чем помещается в ячейку, мы можем легко написать «ссылку» на другую ячейку внутри первой. Такая ссылка называется ref. Мы можем записать до 4 ссылок в ячейку.

Давайте обновим наш код функцией `set_data` и узнаем, как передать в нее ячейку.
```c
() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {
    slice cs = in_msg.begin_parse();
    int flags = cs~load_uint(4);
    slice sender_address = cs~load_msg_addr();
    set_data(begin_cell().end_cell());
}
```
Чтобы передать ячейку в `set_data`, нам нужно сначала ее создать. Это легко сделать с помощью двух функций `begin_cell()` и `end_cell()`.

На данный момент мы передаем пустую ячейку, это технически нормально, но мы хотим записать адрес отправителя сообщения в хранилище, поэтому нам следует обновить нашу ячейку:

```c
() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {
    slice cs = in_msg.begin_parse();
    int flags = cs~load_uint(4);
    slice sender_address = cs~load_msg_addr();
    set_data(begin_cell().store_slice(sender_address).end_cell());
}
```
Для этой цели мы используем метод `.store_slice()`.

Вуаля, у нас есть смарт-контракт, который может записывать адрес отправителя в постоянное хранилище! Каждый раз, когда наш контракт получает внутреннее сообщение, он заменяет ячейку, хранящуюся в c4, новой ячейкой с новым адресом отправителя.

# Использование методов получения

Как вы помните, мы не хотели просто хранить адрес отправителя. Мы хотели, чтобы каждый мог прочитать последний адрес отправителя. Чтобы получить доступ к таким данным за пределами TVM, в нашем контракте должна быть специальная функция.

Недавно мы говорили о спецификаторах функций. Чтобы сделать наши данные доступными за пределами TVM, мы собираемся создать функцию и использовать спецификатор `method_id`. Если у функции установлен этот спецификатор - то ее можно вызвать в lite-клиенте или ton-explorer как get-метод по ее имени.

Давайте создадим один:
```c
slice get_the_latest_sender() method_id {
}
```
> Функции получения расположены вне функции `recv_internal`.

Как видите, мы определяем, какое время должна возвращать эта функция, имя функции и спецификатор `method_id`.

Теперь напишем логику чтения данных из постоянного хранилища и возврата их значения. Для этого мы используем стандартную функцию FunC `get_data`:
```c
slice get_the_latest_sender() method_id {
    slice ds = get_data().begin_parse();
    return ds~load_msg_addr();
}
```
Как видите, мы снова используем метод `begin_parse()`, чтобы получить указатель, по которому мы собираемся проанализировать ячейку, хранящуюся в хранилище c4.

Чтобы загрузить сохраненный адрес, мы используем `~load_msg_addr` для загрузки адреса.

# Составляем наш договор

Наш окончательный код выглядит так:
```c
#include "imports/stdlib.fc";

() recv_internal(int msg_value, cell in_msg, slice in_msg_body) impure {
    slice cs = in_msg.begin_parse();
    int flags = cs~load_uint(4);
    slice sender_address = cs~load_msg_addr();

    set_data(begin_cell().store_slice(sender_address).end_cell());

}

slice get_the_latest_sender() method_id {
    slice ds = get_data().begin_parse();
    return ds~load_msg_addr();
}
```
Довольно простой контракт, но мы многому научились, пока писали его, не так ли?

Раз уж мы написали весь запланированный код — давайте запустим прелесть `bun compile` в нашем терминале.

Если вы сделали все шаг за шагом вместе со мной, вы должны увидеть в терминале следующий результат:

```shell
$ bun ./scripts/compile.ts
============================
Скрипт компиляции запущен...
Компиляция успешна: 80
Скомпилированный код сохранен в  build/main.compiled.json
```
Давайте проверим `build/main.compiled.json` и увидим, что его содержимое изменилось — на этот раз шестнадцатеричное значение намного длиннее :) Это потому, что вы написали свой первый код FunC! Мои поздравления!
